﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Linq;

namespace ImageOCR
{
    /// <summary>
    /// 影像处理器
    /// </summary>
    class ImageOprationControl
    {
        #region 影像处理基本方法

        /// <summary>
        /// 将ARGB图像矩阵bm变为2值图像矩阵im
        /// </summary>
        /// <param name="bm">输入的影像</param>
        /// <param name="t"> 最佳阈值</param>
        /// <returns>输出的目标二值矩阵</returns>
        public static byte[,] ToBinary(Bitmap bm, int t)
        {
            var w = bm.Width;
            var h = bm.Height;
            var im = new byte[w, h];
            var data = bm.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);

            unsafe
            {
                var ptr = (byte*)data.Scan0;
                for (int j = 0; j < h; j++)
                {
                    for (int i = 0; i < w; i++)
                    {
                        im[i, j] = ((ptr[0] + ptr[1] + ptr[2]) / 3) > t ? (byte)0 : (byte)1;//前景色为1，背景色为0
                        ptr += 3;
                    }
                    ptr += data.Stride - w * 3;
                }
            }

            bm.UnlockBits(data);

            return im;
        }

        /// <summary>
        /// 将二值矩阵转换为影像
        /// </summary>
        /// <param name="bs">影像的二值矩阵</param>
        /// <returns>输出的目标影像</returns>
        public static Bitmap ToBitmap(byte[,] bs)
        {
            var w = bs.GetLength(0);
            var h = bs.GetLength(1);
            var bm = new Bitmap(w, h);
            BitmapData data = bm.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb);

            unsafe
            {
                var ptr = (byte*)data.Scan0;
                for (int j = 0; j < h; j++)
                {
                    for (int i = 0; i < w; i++)
                    {
                        ptr[0] = ptr[1] = ptr[2] = bs[i, j] == 1 ? (byte)0 : (byte)255;
                        ptr += 3;
                    }
                    ptr += data.Stride - w * 3;
                }
            }

            bm.UnlockBits(data);

            return bm;
        }

        /// <summary>
        /// 根据灰度平均值获取影像二值化的最佳阈值
        /// </summary>
        /// <param name="bm">输入的影像</param>
        /// <returns>最佳阈值</returns>
        public static int GetThreshold(Bitmap bm)
        {
            var sum = 0.0d;
            var w = bm.Width;
            var h = bm.Height;
            var data = bm.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);

            unsafe
            {
                var ptr = (byte*)data.Scan0.ToPointer();
                for (int j = 0; j < h; j++)
                {
                    for (int i = 0; i < w; i++)
                    {
                        sum += (ptr + j * data.Stride + i * 3)[2];
                    }
                }
            }

            var t = (int)(sum / (w * h) / 2);

            bm.UnlockBits(data);

            return t + 40;//加权，实践证明这样处理的影像更清晰
        }

        /// <summary>
        /// 横向投影（Y轴投影）
        /// </summary>
        public static byte[,] HorizontalProjection(byte[,] im)
        {
            var w = im.GetLength(0);
            var h = im.GetLength(1);
            var gs = new byte[w, h];

            for (int j = 0; j < h; j++)
            {
                var s = 0;
                for (int i = 0; i < w; i++)
                {
                    s += im[i, j];
                }

                for (int i = 0; i < s; i++)
                {
                    gs[i, j] = 1;
                }
            }

            return gs;
        }

        /// <summary>
        /// 纵向投影（X轴投影）
        /// </summary>
        public static byte[,] VerticalProjection(byte[,] im)
        {
            var w = im.GetLength(0);
            var h = im.GetLength(1);
            var gs = new byte[w, h];

            for (int i = 0; i < w; i++)
            {
                var s = 0;
                for (int j = 0; j < h; j++)
                {
                    s += im[i, j];
                }

                for (int j = h - s; j < h; j++)
                {
                    gs[i, j] = 1;
                }
            }

            return gs;
        }

        /// <summary>
        /// 保存分割图片
        /// </summary>
        /// <param name="bm">输入的影像</param>
        /// <param name="rs">矩形内容区集合</param>
        /// <param name="path">保存的路径（以“\”结束）</param>
        public static void SaveImageToTif(Bitmap bm, List<Rectangle> rs, string path)
        {
            var w = bm.Width;
            var h = bm.Height;
            var data = bm.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
            unsafe
            {
                var ptr = (byte*)data.Scan0.ToPointer();
                var n = 0;
                foreach (var rectangle in rs)
                {
                    n++;
                    var obm = new Bitmap(rectangle.Width, rectangle.Height);
                    var obmdata = obm.LockBits(new Rectangle(0, 0, rectangle.Width, rectangle.Height),
                                               ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb);
                    var p = (byte*)obmdata.Scan0.ToPointer();
                    for (int j = rectangle.Y; j < rectangle.Height + rectangle.Y; j++)
                    {
                        for (int i = rectangle.X; i < rectangle.Width + rectangle.X; i++)
                        {
                            byte* _p = p + (j - rectangle.Y) * obmdata.Stride + (i - rectangle.X) * 3;
                            byte* _ptr = ptr + j * data.Stride + i * 3;
                            _p[0] = _ptr[0];
                            _p[1] = _ptr[1];
                            _p[2] = _ptr[2];
                        }
                    }

                    obm.UnlockBits(obmdata);
                    obm.Save(@path + n + ".tif", ImageFormat.Tiff);
                }
            }
            bm.UnlockBits(data);
        }

        /// <summary>
        /// 保存TIF图片
        /// </summary>
        /// <param name="bm">二值化影像</param>
        /// <param name="rs">包含药品的矩形框列表</param>
        /// <param name="path">要保存的路径</param>
        public static void SaveImageToTifForListImage(Bitmap bm, List<Rectangle> rs, string path)
        {
            var w = bm.Width;
            var h = bm.Height;
            var data = bm.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
            unsafe
            {
                var ptr = (byte*)data.Scan0.ToPointer();
                var n = 0;
                foreach (var rectangle in rs)
                {
                    n++;
                    var rw = rectangle.Width;
                    var rh = rectangle.Height;

                    var _jMin = rectangle.Y - 10 < 0 ? rectangle.Y : rectangle.Y - 10;
                    var _iMin = rectangle.X - 10 < 0 ? rectangle.X : rectangle.X - 10;
                    var _jMax = rh + rectangle.Y + 10 > h ? rh + rectangle.Y : rh + rectangle.Y + 10;
                    var _iMax = rw + rectangle.X + 10 > w ? rw + rectangle.X : rw + rectangle.X + 10;

                    var obm = new Bitmap(_iMax - _iMin, _jMax - _jMin);
                    var obmdata = obm.LockBits(new Rectangle(0, 0, _iMax - _iMin, _jMax - _jMin),
                                               ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb);
                    var p = (byte*)obmdata.Scan0.ToPointer();
                    for (int j = _jMin; j < _jMax; j++)
                    {
                        for (int i = _iMin; i < _iMax; i++)
                        {
                            var _p = p + (j - _jMin) * rw * 3 + (i - _iMin) * 3 +
                                     (obmdata.Stride - rw * 3) * (j - _jMin);
                            _p[0] = (ptr + j * data.Stride + i * 3)[0];
                            _p[1] = (ptr + j * data.Stride + i * 3)[1];
                            _p[2] = (ptr + j * data.Stride + i * 3)[2];
                        }
                    }

                    obm.UnlockBits(obmdata);
                    obm.Save(@path + n + ".tif", ImageFormat.Tiff);
                }
            }
            bm.UnlockBits(data);
        }

        /// <summary>
        /// 横向二值采样
        /// </summary>
        /// <param name="bm">输入的图像</param>
        /// <param name="space">采样间隔</param>
        /// <returns>输出的目标图像</returns>
        public static Bitmap Horizontal2ValueSampling(Bitmap bm, int space)
        {
            var w = bm.Width;
            var h = bm.Height;
            BitmapData data = bm.LockBits(new Rectangle(0, 0, bm.Width, bm.Height), ImageLockMode.ReadWrite,
                                          PixelFormat.Format24bppRgb);
            int stride = data.Stride;

            unsafe
            {
                var ptr = (byte*)data.Scan0.ToPointer();
                for (int j = 0; j < h; j++)
                {
                    for (int i = 0; i < w; i = i + space)
                    {
                        int c = 0;
                        for (int k = 0; k < space; k++)
                        {
                            var x = i + k >= w ? w - 1 : i + k;
                            c = c +
                                ((ptr + j * stride + (x * 3))[0] + (ptr + j * stride + (x * 3))[1] + (ptr + j * stride + (x * 3))[2]) /
                                3;
                        }

                        c = c / space;

                        for (int k = 0; k < space; k++)
                        {
                            var x = i + k >= w ? w - 1 : i + k;
                            (ptr + j * stride + (x * 3))[0] =
                                (ptr + j * stride + (x * 3))[1] =
                                (ptr + j * stride + (x * 3))[2] = c < 255 ? (byte)0 : (byte)255;
                        }
                    }
                }
            }

            bm.UnlockBits(data);

            return bm;
        }

        /// <summary>
        /// 找左边竖线
        /// </summary>
        /// <param name="bm">输入的采样后影像 </param>
        /// <param name="im">影像的二值矩阵 </param>
        /// <param name="x">边界像素占竖行的百分比</param>
        public static Bitmap FindLeftLines(Bitmap bm, byte[,] im, float x)
        {
            var w = bm.Width;
            var h = bm.Height;
            var gs = new int[w, h];
            BitmapData data = bm.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.ReadWrite,
                                           PixelFormat.Format24bppRgb);

            unsafe
            {
                var p = (byte*)data.Scan0.ToPointer();
                for (int i = 1; i < w - 1; i++)
                {
                    for (int j = 1; j < h - 1; j++)
                    {
                        if (im[i - 1, j] == 0 && im[i + 1, j] == 1)
                        {
                            gs[i, j] = 1;
                        }
                    }
                }

                for (int i = 0; i < w; i++)
                {
                    var s = 0;
                    for (int j = 0; j < h; j++)
                    {
                        s += gs[i, j];
                    }

                    if ((float)s / h > x)
                    {
                        for (int j = 0; j < h; j++)
                        {
                            (p + data.Stride * j + i * 3)[0] = 0;
                            (p + data.Stride * j + i * 3)[1] = 0;
                            (p + data.Stride * j + i * 3)[2] = 255;
                        }
                    }
                }
            }

            bm.UnlockBits(data);

            return bm;
        }

        /// <summary>
        /// 将横向空白区画横线
        /// </summary>
        /// <param name="bm">输入的采样后影像 </param>
        /// <param name="im">影像的二值矩阵 </param>
        /// <param name="x">每行（像素行）允许的噪点百分比（如：0.1f）</param>
        public static Bitmap FindTransverseSpace(Bitmap bm, byte[,] im, float x)
        {
            var w = bm.Width;
            var h = bm.Height;
            BitmapData data = bm.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.ReadWrite,
                                           PixelFormat.Format24bppRgb);

            unsafe
            {
                var p = (byte*)data.Scan0.ToPointer();
                for (int j = 0; j < h; j++)
                {
                    var s = 0;
                    var ptr = p + data.Stride * j;
                    for (int i = 0; i < w; i++)
                    {
                        s = s + im[i, j];
                    }
                    if ((float)s / w < x)
                    {
                        for (int i = 0; i < w; i++)
                        {
                            ptr[0] = 0;
                            ptr[1] = 0;
                            ptr[2] = 255;
                            ptr += 3;
                        }
                    }
                }
            }

            bm.UnlockBits(data);

            return bm;
        }

        /// <summary>
        /// 红色区域涂白
        /// </summary>
        public static Bitmap Getfield(Bitmap bm)
        {
            var w = bm.Width;
            var h = bm.Height;
            var gs = new int[w, h];
            BitmapData data = bm.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.ReadWrite,
                                           PixelFormat.Format24bppRgb);

            unsafe
            {
                var p = (byte*)data.Scan0.ToPointer();
                for (int i = 0; i < w; i++)
                {
                    for (int j = 0; j < h; j++)
                    {
                        if ((p + j * data.Stride + i * 3)[0] == 0 && (p + j * data.Stride + i * 3)[1] == 0 && (p + j * data.Stride + i * 3)[2] == 255)
                        {
                            gs[i, j] = 1;
                        }
                    }
                }

                for (int i = 0; i < w; i++)
                {
                    for (int j = 0; j < h; j++)
                    {
                        (p + j * data.Stride + i * 3)[0] =
                                (p + j * data.Stride + i * 3)[1] = (p + j * data.Stride + i * 3)[2] = gs[i, j] == 0 ? (byte)0 : (byte)255;
                    }
                }
            }

            bm.UnlockBits(data);

            return bm;
        }

        /// <summary>
        /// 找黑色矩形快
        /// </summary>
        /// <param name="bm">输入的影像 </param>
        /// <returns>输出的目标图像</returns>
        public static List<Rectangle> FindRectangle(Bitmap bm)
        {
            var w = bm.Width;
            var h = bm.Height;
            var rectangles = new List<Rectangle>();
            var im = ToBinary(bm, GetThreshold(bm));

            for (int j = 0; j < h - 1; j++)
            {
                for (int i = 0; i < w - 1; i++)
                {
                    if (i > 0 && j > 0)
                    {
                        if (im[i, j] == 0) { continue; }
                        if (im[i, j - 1] == 1) { continue; }
                        if (im[i + 1, j - 1] == 1) { continue; }
                        if (im[i + 1, j] == 0) { continue; }
                        if (im[i + 1, j + 1] == 0) { continue; }
                        if (im[i, j + 1] == 0) { continue; }
                        if (im[i - 1, j + 1] == 1) { continue; }
                        if (im[i - 1, j] == 1) { continue; }
                        if (im[i - 1, j - 1] == 1) { continue; }
                    }
                    else if (i == 0 && j == 0)
                    {
                        if (im[i, j] == 0) { continue; }
                    }
                    else if (i == 0 && j > 0)
                    {
                        if (im[i, j] == 0) { continue; }
                        if (im[i, j - 1] == 1) { continue; }
                        if (im[i + 1, j - 1] == 1) { continue; }
                        if (im[i + 1, j] == 0) { continue; }
                        if (im[i + 1, j + 1] == 0) { continue; }
                        if (im[i, j + 1] == 0) { continue; }
                    }
                    else if (j == 0 && i > 0)
                    {
                        if (im[i, j] == 0) { continue; }
                        if (im[i + 1, j] == 0) { continue; }
                        if (im[i + 1, j + 1] == 0) { continue; }
                        if (im[i, j + 1] == 0) { continue; }
                        if (im[i - 1, j + 1] == 1) { continue; }
                        if (im[i - 1, j] == 1) { continue; }
                    }

                    var x = i;
                    var flagX = true;
                    do
                    {
                        if (x == w) break;
                        if (im[x, j] == 0)
                        {
                            flagX = false;
                        }
                        x++;
                    } while (flagX);

                    var y = j;
                    var flagY = true;
                    do
                    {
                        if (y == h) break;
                        if (im[i, y] == 0)
                        {
                            flagY = false;
                        }
                        y++;
                    } while (flagY);

                    if (y - j < 16) { continue; }
                    if (x - i < w / 8) { continue; }
                    if (x == w) { continue; }
                    if (y == h) { continue; }

                    rectangles.Add(new Rectangle(i, j, x - i, y - j));
                }
            }

            return rectangles;
        }

        /// <summary>
        /// 二值形态膨胀
        /// </summary>
        /// <param name="bm">输入的影像</param>
        /// <param name="im">输入影像的二值矩阵</param>
        /// <param name="type">1 表示3X3正方结构，2 表示菱形结构，3 表示5X5八角结构</param>
        /// <returns></returns>
        public static byte[,] Dilate(Bitmap bm, byte[,] im, int type)
        {
            var w = bm.Width;
            var h = bm.Height;
            var outp = new byte[w, h];

            switch (type)
            {
                //正方结构
                case 1:
                    for (int j = 1; j < h - 1; j++)
                    {
                        for (int i = 1; i < w - 1; i++)
                        {
                            var s = im[i, j];
                            if (s == 1)
                            {
                                outp[i, j] = s;
                                continue;
                            }
                            if (im[i - 1, j - 1] == 1) { outp[i, j] = 1; continue; }
                            if (im[i - 1, j] == 1) { outp[i, j] = 1; continue; }
                            if (im[i - 1, j + 1] == 1) { outp[i, j] = 1; continue; }
                            if (im[i, j - 1] == 1) { outp[i, j] = 1; continue; }
                            if (im[i - 1, j + 1] == 1) { outp[i, j] = 1; continue; }
                            if (im[i + 1, j - 1] == 1) { outp[i, j] = 1; continue; }
                            if (im[i + 1, j] == 1) { outp[i, j] = 1; continue; }
                            if (im[i + 1, j + 1] == 1) { outp[i, j] = 1; }
                        }
                    }
                    break;
                //菱形结构
                case 2:
                    for (int j = 1; j < h - 1; j++)
                    {
                        for (int i = 1; i < w - 1; i++)
                        {
                            var s = im[i, j];
                            if (s == 1)
                            {
                                outp[i, j] = s;
                                continue;
                            }
                            if (im[i - 1, j] == 1) { outp[i, j] = 1; continue; }
                            if (im[i + 1, j] == 1) { outp[i, j] = 1; continue; }
                            if (im[i, j - 1] == 1) { outp[i, j] = 1; continue; }
                            if (im[i, j + 1] == 1) { outp[i, j] = 1; }
                        }
                    }
                    break;
                //5X5八角结构
                case 3:
                    for (int j = 2; j < h - 2; j++)
                    {
                        for (int i = 2; i < w - 2; i++)
                        {
                            var s = im[i, j];
                            if (s == 1)
                            {
                                outp[i, j] = s;
                                continue;
                            }
                            if (im[i - 2, j - 1] == 1) { outp[i, j] = 1; continue; }
                            if (im[i - 2, j] == 1) { outp[i, j] = 1; continue; }
                            if (im[i - 2, j + 1] == 1) { outp[i, j] = 1; continue; }

                            if (im[i - 1, j - 2] == 1) { outp[i, j] = 1; continue; }
                            if (im[i - 1, j - 1] == 1) { outp[i, j] = 1; continue; }
                            if (im[i - 1, j] == 1) { outp[i, j] = 1; continue; }
                            if (im[i - 1, j + 1] == 1) { outp[i, j] = 1; continue; }
                            if (im[i - 1, j + 2] == 1) { outp[i, j] = 1; continue; }

                            if (im[i, j - 2] == 1) { outp[i, j] = 1; continue; }
                            if (im[i, j - 1] == 1) { outp[i, j] = 1; continue; }
                            if (im[i, j + 1] == 1) { outp[i, j] = 1; continue; }
                            if (im[i, j + 2] == 1) { outp[i, j] = 1; continue; }

                            if (im[i + 1, j - 2] == 1) { outp[i, j] = 1; continue; }
                            if (im[i + 1, j - 1] == 1) { outp[i, j] = 1; continue; }
                            if (im[i + 1, j] == 1) { outp[i, j] = 1; continue; }
                            if (im[i + 1, j + 1] == 1) { outp[i, j] = 1; continue; }
                            if (im[i + 1, j + 2] == 1) { outp[i, j] = 1; continue; }

                            if (im[i + 2, j - 1] == 1) { outp[i, j] = 1; continue; }
                            if (im[i + 2, j] == 1) { outp[i, j] = 1; continue; }
                            if (im[i + 2, j + 1] == 1) { outp[i, j] = 1; }
                        }
                    }
                    break;
            }

            return outp;
        }

        /// <summary>
        /// 二值形态腐蚀
        /// </summary>
        /// <param name="im">输入二值图像</param>
        /// <param name="w">输入图像宽</param>
        /// <param name="h">输入图像高</param>
        /// <param name="type">1 表示3X3正文形结构,2 表示菱形结构,3 表示5X5八角结构,4 空心的3X3结构，用于HMT变换</param>
        /// <returns>输出目标图像</returns>
        public static byte[,] Erode(byte[,] im, int w, int h, int type)
        {
            var outp = new byte[w, h];

            //正文形结构
            switch (type)
            {
                case 1:
                    for (int j = 1; j < h - 1; j++)
                    {
                        for (int i = 1; i < w - 1; i++)
                        {
                            byte s = im[i, j];
                            if (s == 0)
                            {
                                continue;
                            }

                            if (im[i - 1, j - 1] == 0)
                                continue;
                            if (im[i - 1, j] == 0)
                                continue;
                            if (im[i - 1, j + 1] == 0)
                                continue;
                            if (im[i, j - 1] == 0)
                                continue;
                            if (im[i + 1, j - 1] == 0)
                                continue;
                            if (im[i + 1, j] == 0)
                                continue;
                            if (im[i + 1, j + 1] == 0)
                                continue;

                            outp[i, j] = 1;
                        }
                    }
                    break;
                case 2:
                    for (int j = 1; j < h - 1; j++)
                    {
                        for (int i = 1; i < w - 1; i++)
                        {
                            byte s = im[i, j];
                            if (s == 0)
                                continue;
                            if (im[i - 1, j] == 0)
                                continue;
                            if (im[i + 1, j] == 0)
                                continue;
                            if (im[i, j - 1] == 0)
                                continue;
                            if (im[i, j + 1] == 0)
                                continue;

                            outp[i, j] = 1;
                        }
                    }
                    break;
                case 3:
                    for (int j = 2; j < h - 2; j++)
                    {
                        for (int i = 2; i < w - 2; i++)
                        {
                            byte s = im[i, j];
                            if (s == 0)
                                continue;

                            if (im[i - 2, j - 1] == 0) continue;
                            if (im[i - 2, j] == 0) continue;
                            if (im[i - 2, j + 1] == 0) continue;

                            if (im[i - 1, j - 2] == 0) continue;
                            if (im[i - 1, j - 1] == 0) continue;
                            if (im[i - 1, j] == 0) continue;
                            if (im[i - 1, j + 1] == 0) continue;
                            if (im[i - 1, j + 2] == 0) continue;

                            if (im[i, j - 2] == 0) continue;
                            if (im[i, j - 1] == 0) continue;
                            if (im[i, j + 1] == 0) continue;
                            if (im[i, j + 2] == 0) continue;

                            if (im[i + 1, j - 2] == 0) continue;
                            if (im[i + 1, j - 1] == 0) continue;
                            if (im[i + 1, j] == 0) continue;
                            if (im[i + 1, j + 1] == 0) continue;
                            if (im[i + 1, j + 2] == 0) continue;

                            if (im[i + 2, j - 1] == 0) continue;
                            if (im[i + 2, j] == 0) continue;
                            if (im[i + 2, j + 1] == 0) continue;

                            outp[i, j] = 1;
                        }
                    }
                    break;
                case 15:
                    for (int j = 7; j < h - 7; j++)
                    {
                        for (int i = 7; i < w - 7; i++)
                        {
                            bool bf = true;
                            if (im[i, j] == 0) continue;
                            for (int k = -7; k < 8; k++)
                            {
                                for (int l = -7; l < 8; l++)
                                {
                                    if (im[i + k, j + l] == 0)
                                    {
                                        bf = false;
                                        break;
                                    }

                                    if (bf)
                                    {
                                        outp[i, j] = 1;
                                    }
                                }
                            }
                        }
                    }
                    break;
            }

            return outp;
        }

        #endregion

        #region 影像纠偏方法

        /// <summary>
        /// 影像纠偏方法
        /// </summary>
        /// <param name="bm">输入的影像</param>
        /// <returns>纠偏后的影像</returns>
        public static Bitmap ImageRectification(Bitmap bm)
        {
            var w = bm.Width;
            var h = bm.Height;
            var im = ToBinary(bm, GetThreshold(bm));
            var hLines = findAllHorizontalLines(im, ConfigOpration.LineSpace, w / ConfigOpration.HLineMinLengthRatio, w);//找横线
            var vLines = findAllVerticalLines(im, ConfigOpration.LineSpace, h / ConfigOpration.VLineMinLengthRatio, h);//找竖线
            var degree = GetDegree(hLines, vLines);
            var d = Math.Round(degree / Math.PI * 180, 2);
            var obm = RotateImg(bm, d);
            return obm;
        }

        /// <summary>
        /// 图像旋转
        /// </summary>
        /// <param name="bm">影像</param>
        /// <param name="angle">旋转角度</param>
        /// <returns>旋转后的影像</returns>
        public static Bitmap RotateImg(Bitmap bm, double angle)
        {
            angle = angle % 360;

            //弧度转换
            double radian = angle * Math.PI / 180.0;
            double cos = Math.Cos(radian);
            double sin = Math.Sin(radian);

            //原图的宽和高
            int w = bm.Width;
            int h = bm.Height;
            var W = (int)(Math.Max(Math.Abs(w * cos - h * sin), Math.Abs(w * cos + h * sin)));
            var H = (int)(Math.Max(Math.Abs(w * sin - h * cos), Math.Abs(w * sin + h * cos)));

            //目标位图
            var dsImage = new Bitmap(W, H);
            var dataObm = dsImage.LockBits(new Rectangle(0, 0, W, H), ImageLockMode.ReadWrite,
                                       PixelFormat.Format24bppRgb);

            unsafe
            {
                var ptr1 = (byte*)dataObm.Scan0.ToPointer();
                for (int j = 0; j < H; j++)
                {
                    for (int i = 0; i < W; i++)
                    {
                        var p2 = ptr1 + j * dataObm.Stride + i * 3;
                        p2[2] = 255;
                        p2[1] = 255;
                        p2[0] = 255;
                    }
                }
            }

            dsImage.UnlockBits(dataObm);

            Graphics g = Graphics.FromImage(dsImage);
            g.InterpolationMode = InterpolationMode.Bilinear;
            g.SmoothingMode = SmoothingMode.HighQuality;

            //计算偏移量
            var offset = new Point((W - w) / 2, (H - h) / 2);

            //构造图像显示区域：让图像的中心与窗口的中心点一致
            var rect = new Rectangle(offset.X, offset.Y, w, h);
            var center = new Point(rect.X + rect.Width / 2, rect.Y + rect.Height / 2);

            g.TranslateTransform(center.X, center.Y);
            g.RotateTransform((float)(360 - angle));

            //恢复图像在水平和垂直方向的平移
            g.TranslateTransform(-center.X, -center.Y);
            g.DrawImage(bm, rect);

            //重至绘图的所有变换
            g.ResetTransform();

            g.Save();
            g.Dispose();
            //保存旋转后的图片
            bm.Dispose();

            return dsImage;
        }

        /// <summary>
        /// 分析影像偏向角度
        /// </summary>
        /// <param name="hLines">直线集合</param>
        /// <param name="vLines">竖线集合</param>
        /// <returns>偏向角度（向下为正，向上为负）</returns>
        public static double GetDegree(List<Line> hLines, List<Line> vLines)
        {
            var degreeArry = new List<double>();//角度列表
            var direction = new List<int>();//判断图像旋转方向
            var listClusterLines1 = new List<List<Line>>();
            var listClusterLines2 = new List<List<Line>>();
            var hCount = hLines.Count;
            var vCount = vLines.Count;
            if (hLines.Count > 0)
            {
                listClusterLines1 = findClusterLines(hLines);
            }
            if (vLines.Count > 0)
            {
                listClusterLines2 = findClusterLines(vLines);
            }

            if (listClusterLines1.Count > 0 && hCount / listClusterLines1.Count < 100 && hCount / listClusterLines1.Count > 15)
            {
                foreach (var listClusterLine in listClusterLines1)
                {
                    if (listClusterLine.Count < 15 || listClusterLine.Count > 60)
                    {
                        continue;
                    }
                    var min = listClusterLine[0].BMax - listClusterLine[0].BMin;
                    var minLine = listClusterLine[0];
                    foreach (var line in listClusterLine)
                    {
                        if (min > line.BMax - line.BMin)
                        {
                            min = line.BMax - line.BMin;
                            minLine = line;
                        }
                    }

                    var max = listClusterLine[0].BMax - listClusterLine[0].BMin;
                    var maxLine = listClusterLine[0];
                    foreach (var line in listClusterLine)
                    {
                        if (max < line.BMax - line.BMin)
                        {
                            max = line.BMax - line.BMin;
                            maxLine = line;
                        }
                    }

                    if (minLine.A < maxLine.A)
                    {
                        direction.Add(-1);
                    }
                    else
                    {
                        direction.Add(1);
                    }

                    //分析角度
                    var a = listClusterLine.Count;
                    var b = max - min;
                    double d = Math.Atan((double)a / b);
                    degreeArry.Add(d);
                }
            }

            if (listClusterLines2.Count > 0 && vCount / listClusterLines2.Count < 100 && vCount / listClusterLines2.Count > 15)
            {
                foreach (var listClusterLine in listClusterLines2)
                {
                    if (listClusterLine.Count < 15 || listClusterLine.Count > 100)
                    {
                        continue;
                    }
                    var min = listClusterLine[0].BMax - listClusterLine[0].BMin;
                    var minLine = listClusterLine[0];
                    foreach (var line in listClusterLine)
                    {
                        if (min > line.BMax - line.BMin)
                        {
                            min = line.BMax - line.BMin;
                            minLine = line;
                        }
                    }

                    var max = listClusterLine[0].BMax - listClusterLine[0].BMin;
                    var maxLine = listClusterLine[0];
                    foreach (var line in listClusterLine)
                    {
                        if (max < line.BMax - line.BMin)
                        {
                            max = line.BMax - line.BMin;
                            maxLine = line;
                        }
                    }

                    if (minLine.A > maxLine.A)
                    {
                        direction.Add(-1);
                    }
                    else
                    {
                        direction.Add(1);
                    }

                    //分析角度
                    var a = listClusterLine.Count;
                    var b = max - min;
                    double d = Math.Atan((double)a / b);
                    degreeArry.Add(d);
                }
            }

            if (degreeArry.Count < 3)
            {
                return 0;
            }

            double s = 0.00d;
            for (int i = 0; i < degreeArry.Count; i++)
            {
                s += degreeArry[i];
            }

            if (degreeArry.Count > 0)
            {
                double degree = Math.Round(s / degreeArry.Count, 2);

                //超过5度不予理会
                if (Math.Abs(degree) > 5)
                {
                    return 0;
                }

                var v = 0;
                for (int i = 0; i < direction.Count; i++)
                {
                    v += direction[i];
                }

                if (v < 0)
                {
                    return -degree;
                }

                return degree;
            }

            return 0;
        }

        /// <summary>
        /// 找同簇直线
        /// </summary>
        /// <param name="lines">直线集合</param>
        /// <returns>同簇直线列表</returns>
        private static List<List<Line>> findClusterLines(List<Line> lines)
        {
            var listClusterLines = new List<List<Line>>();
            for (int i = 0; i < lines.Count; i++)
            {
                var custerLines = new List<Line> { lines[i] };
                findLinesCluster(ref lines, ref custerLines, lines[i]);
                listClusterLines.Add(custerLines);
                lines.Remove(lines[i]);
            }
            return listClusterLines;
        }

        /// <summary>
        /// 找同簇直线
        /// </summary>
        /// <param name="lines">直线集合</param>
        /// <param name="clusterLines">同簇直线集合 </param>
        /// <param name="tempLine"> 模板直线</param>
        /// <returns>直线簇</returns>
        private static void findLinesCluster(ref List<Line> lines, ref List<Line> clusterLines, Line tempLine)
        {
            var afterLines = lines.FindAll(l => l.A == tempLine.A + 1);
            if (afterLines.Any())
            {
                var waitFixLines = new List<Line>();
                foreach (var afterLine in afterLines)
                {
                    if (isOnSameCluster(tempLine, afterLine))
                    {
                        waitFixLines.Add(afterLine);
                    }
                }

                if (waitFixLines.Count >= 2)
                {
                    var newLine = fixLine(waitFixLines);
                    clusterLines.Add(newLine);
                    lines.Remove(tempLine);
                    findLinesCluster(ref lines, ref clusterLines, newLine);
                }
                else if (waitFixLines.Count == 1)
                {
                    clusterLines.Add(waitFixLines[0]);
                    lines.Remove(tempLine);
                    findLinesCluster(ref lines, ref clusterLines, waitFixLines[0]);
                }
            }
        }

        /// <summary>
        /// 修复直线
        /// </summary>
        /// <param name="lines">同直线上的直线集合</param>
        /// <returns>修复后的直线</returns>
        private static Line fixLine(List<Line> lines)
        {
            var newLine = new Line { A = lines[0].A };
            var min = lines[0].BMin;
            foreach (var line in lines)
            {
                if (min > line.BMin)
                {
                    min = line.BMin;
                }
            }
            var max = lines[0].BMax;
            foreach (var line in lines)
            {
                if (max < line.BMax)
                {
                    max = line.BMax;
                }
            }

            newLine.BMin = min;
            newLine.BMax = max;

            return newLine;
        }

        #endregion

        #region 影像线条处理

        /// <summary>
        /// 找横线
        /// </summary>
        /// <param name="im">影像的二值矩阵</param>
        /// <param name="space">允许断开的距离</param>
        /// <param name="minLength">直线最小长度</param>
        /// <param name="maxLength">直线最大长度</param>
        /// <returns>细化后的横线集合</returns>
        public static List<Line> FindHorizontalLines(byte[,] im, int space, int minLength, int maxLength)
        {
            //找到影像里所有的横线
            List<Line> hLines = findAllHorizontalLines(im, space, minLength, maxLength);

            var tinnerHLines = new List<Line>();
            if (hLines.Count > 0)
            {
                //横线细化处理
                tinnerHLines = getTinnerLines(hLines);
            }

            return tinnerHLines;
        }

        /// <summary>
        /// 找竖线
        /// </summary>
        /// <param name="im">影像的二值矩阵</param>
        /// <param name="space">允许断开的距离</param>
        /// <param name="minLength">直线最小长度</param>
        /// <param name="maxLength">直线最大长度</param>
        /// <returns>细化后的竖线集合</returns>
        public static List<Line> FindVerticalLines(byte[,] im, int space, int minLength, int maxLength)
        {
            //找到影像里所有的横线
            List<Line> vLines = findAllVerticalLines(im, space, minLength, maxLength);

            var tinnerVLines = new List<Line>();
            if (vLines.Count > 0)
            {
                //竖线细化处理
                tinnerVLines = getTinnerLines(vLines);
            }

            return tinnerVLines;
        }

        #region 找线及细化方法

        /// <summary>
        /// 找横线
        /// </summary>
        /// <param name="im">影像的二值矩阵</param>
        /// <param name="space">允许断开的距离</param>
        /// <param name="minLength">直线最小长度</param>
        /// <param name="maxLength">直线最大长度</param>
        /// <returns>横线集合</returns>
        private static List<Line> findAllHorizontalLines(byte[,] im, int space, int minLength, int maxLength)
        {
            var w = im.GetLength(0);
            var h = im.GetLength(1);
            var lines = new List<Line>();

            for (int j = 1; j < h - 1; j++)
            {
                for (int i = 1; i < w - space; i++)
                {
                    if (im[i, j] == 1)
                    {
                        var line = new Line { A = j, BMin = i, BMax = i };
                        var flag = true;
                        var y = j;
                        while (flag)
                        {
                            i++;
                            if (i > w - space - 1) break;
                            flag = false;
                            if (im[i, y] == 1)
                            {
                                flag = true;
                                line.BMax = i;
                            }
                            else
                            {
                                if (y - 1 > 1 && im[i, y - 1] == 1)
                                {
                                    flag = true;
                                    y = y - 1;
                                    line.BMax = i;
                                    continue;
                                }

                                if (y + 1 < h - 1 && im[i, y + 1] == 1)
                                {
                                    flag = true;
                                    y = y + 1;
                                    line.BMax = i;
                                    continue;
                                }

                                for (int k = space; k > 0; k--)
                                {
                                    if (im[i + k, y] == 1)
                                    {
                                        flag = true;
                                        line.BMax = i + k;
                                        i = i + k;
                                        break;
                                    }

                                    if (y - 1 > 1 && im[i + k, y - 1] == 1)
                                    {
                                        flag = true;
                                        y = y - 1;
                                        line.BMax = i + k;
                                        i = i + k;
                                        break;
                                    }

                                    if (y + 1 < h - 1 && im[i + k, y + 1] == 1)
                                    {
                                        flag = true;
                                        y = y + 1;
                                        line.BMax = i + k;
                                        i = i + k;
                                        break;
                                    }
                                }
                            }
                        }

                        var length = line.BMax - line.BMin;
                        if (length > minLength && length < maxLength)
                        {
                            lines.Add(line);
                        }
                    }
                }
            }

            return lines;
        }

        /// <summary>
        /// 找竖线
        /// </summary>
        /// <param name="im">影像的二值矩阵</param>
        /// <param name="space">允许断开的距离</param>
        /// <param name="minLength">直线最小长度</param>
        /// <param name="maxLength">直线最大长度</param>
        /// <returns>竖线集合</returns>
        private static List<Line> findAllVerticalLines(byte[,] im, int space, int minLength, int maxLength)
        {
            var w = im.GetLength(0);
            var h = im.GetLength(1);
            var lines = new List<Line>();

            for (int i = 1; i < w - 1; i++)
            {
                for (int j = 1; j < h - space; j++)
                {
                    if (im[i, j] == 1)
                    {
                        var line = new Line { A = i, BMin = j, BMax = j };
                        var flag = true;
                        var x = i;
                        while (flag)
                        {
                            j++;
                            if (j > h - space - 1) break;
                            flag = false;
                            if (im[x, j] == 1)
                            {
                                flag = true;
                                line.BMax = j;
                            }
                            else
                            {
                                if (x - 1 > 1 && im[x - 1, j] == 1)
                                {
                                    flag = true;
                                    x = x - 1;
                                    line.BMax = j;
                                    continue;
                                }

                                if (x + 1 < w - 1 && im[x + 1, j] == 1)
                                {
                                    flag = true;
                                    x = x + 1;
                                    line.BMax = j;
                                    continue;
                                }

                                for (int k = space; k > 0; k--)
                                {
                                    if (im[i, j + k] == 1)
                                    {
                                        flag = true;
                                        line.BMax = j + k;
                                        j = j + k;
                                        break;
                                    }

                                    if (x - 1 > 1 && im[x - 1, j + k] == 1)
                                    {
                                        flag = true;
                                        x = x - 1;
                                        line.BMax = j + k;
                                        j = j + k;
                                        break;
                                    }

                                    if (x + 1 < w - 1 && im[x + 1, j + k] == 1)
                                    {
                                        flag = true;
                                        x = x + 1;
                                        line.BMax = j + k;
                                        j = j + k;
                                        break;
                                    }
                                }
                            }
                        }

                        var length = line.BMax - line.BMin;
                        if (length > minLength && length < maxLength)
                        {
                            lines.Add(line);
                        }
                    }
                }
            }

            return lines;
        }

        /// <summary>
        /// 直线细化算法，找边界线，删除边界线，处理直线的长度（取最长的直线）
        /// </summary>
        /// <param name="lines">所有直线集合</param>
        /// <returns>细化后的直线集合</returns>
        private static List<Line> getTinnerLines(List<Line> lines)
        {
            var tLines = new List<Line>();

            int i;
            for (i = 0; i < lines.Count; )
            {
                var tempLine = lines[i];
                tempLine = getTempLine(lines, tempLine);
                var n = 0;
                var line = getTinnerLine(ref lines, tempLine, ref n);
                line.A = line.A + n / 2;
                tLines.Add(line);
            }

            return tLines;
        }

        /// <summary>
        /// 获取模板直线，模版直线的长度等于直线簇的范围长度
        /// </summary>
        /// <param name="lines">所有直线集合</param>
        /// <param name="tempLine">模板基线</param>
        /// <returns>模板直线</returns>
        private static Line getTempLine(List<Line> lines, Line tempLine)
        {
            var afterLines = lines.FindAll(l => l.A == tempLine.A + 1);

            if (afterLines.Count > 0)
            {
                var flag = false;
                foreach (Line line in afterLines)
                {
                    if (isOnSameCluster(tempLine, line))
                    {
                        flag = true;
                        tempLine = setLinesLengthAsBigger(tempLine, line, line.A);
                    }
                }

                if (flag)
                {
                    tempLine = getTempLine(lines, tempLine);
                }
            }

            return tempLine;
        }

        /// <summary>
        /// 根据模版直线细化直线簇，得到细化后的直线
        /// </summary>
        /// <param name="lines">所有直线集合</param>
        /// <param name="n">模版直线位移量，用于找中线，将细化后的直线定位到细化前直线簇的中间位置</param>
        /// <param name="tempLine">模板基线</param>
        /// <returns>细化后的直线</returns>
        private static Line getTinnerLine(ref List<Line> lines, Line tempLine, ref int n)
        {
            //反向查找，保证不漏查
            var beforeLines = lines.FindAll(l => l.A == tempLine.A);

            if (beforeLines.Count > 0)
            {
                //是否执行递归标志，当找到的直线中有与模板线同簇时，继续执行递归，否则认为当前直线簇已找完
                var flag = false;

                //相邻直线可能有多条，所以循环
                foreach (Line line in beforeLines)
                {
                    if (isOnSameCluster(tempLine, line))
                    {
                        flag = true;
                        tempLine = setLinesLengthAsBigger(tempLine, line, line.A);
                        //满足条件的直线都从直线集合中移除，方便找细化下个直线簇
                        lines.Remove(line);
                    }
                }

                if (flag)
                {
                    //找到相邻直线后，模板直线相应左移
                    tempLine.A = tempLine.A - 1;
                    n++;
                    //递归，找下一条或多条相邻直线
                    tempLine = getTinnerLine(ref lines, tempLine, ref n);
                }
            }

            return tempLine;
        }

        /// <summary>
        /// 设置直线长度，取两直线的范围长
        /// </summary>
        /// <param name="line1">第一条直线</param>
        /// <param name="line2">第二条直线</param>
        /// <param name="lineA">同轴坐标</param>
        /// <returns>设置长度后的直线</returns>
        private static Line setLinesLengthAsBigger(Line line1, Line line2, int lineA)
        {
            var line = new Line
            {
                A = lineA,
                BMin = line1.BMin < line2.BMin ? line1.BMin : line2.BMin,
                BMax = line1.BMax > line2.BMax ? line1.BMax : line2.BMax
            };

            return line;
        }

        #endregion

        #region 去噪音线方法

        /// <summary>
        /// 根据阈值(两线之间的最小距离)删除噪音线
        /// </summary>
        /// <param name="lines">要除噪音线的集合</param>
        /// <param name="verticalLines">与除噪音线集合垂直的线集合</param>
        /// <param name="x">阈值（两条线相隔最小距离）</param>
        /// <param name="minLength">孤立线最大长度</param>
        /// <returns>去除噪音线后的直线集合</returns>
        public static List<Line> DeleteNoiseLines(List<Line> lines, List<Line> verticalLines, int x, int minLength)
        {
            for (int i = 0; i < lines.Count; i++)
            {
                //判断是否是孤立线
                if (isIsolateLine(lines[i], verticalLines, minLength))
                {
                    lines.Remove(lines[i]);
                    i--;
                    continue;
                }
                if (removeNoiseLine(ref lines, lines[i], verticalLines, x))
                {
                    i--;
                }
            }

            return lines;
        }

        /// <summary>
        /// 删除阈值内两条直线中较短的直线，并返回是否成功移除
        /// </summary>
        /// <param name="lines">所有要除噪音线的直线集合</param>
        /// <param name="line">基线</param>
        /// <param name="verticalLines">与除噪音线集合垂直的线集合</param>
        /// <param name="x">阈值</param>
        /// <returns>是否移除</returns>
        private static bool removeNoiseLine(ref List<Line> lines, Line line, List<Line> verticalLines, int x)
        {
            var flag = false;
            for (int i = -x; i < x; i++)
            {
                if (i == 0) continue;
                var tempLines = lines.FindAll(l => l.A == line.A + i);

                if (tempLines.Count > 0)
                {
                    foreach (var tempLine in tempLines)
                    {
                        //判断两线是否同簇，不同簇的不予处理
                        if (isOnSameCluster(tempLine, line))
                        {
                            var noiseLine = getSortLine(line, tempLine);
                            //如果此线有两个交点，则认为不是噪音线
                            var vLines =
                                    verticalLines.FindAll(
                                        l =>
                                        (l.A <= noiseLine.BMax && l.A >= noiseLine.BMin) &&
                                        (l.BMin <= noiseLine.A && l.BMax >= noiseLine.A));
                            if (vLines.Count <= 1 || (Math.Abs(line.A - tempLine.A) < 10))//可以允许有一个交点，两线平行距离小于10像素的必删除一条
                            {
                                flag = true;
                                lines.Remove(getSortLine(line, tempLine));
                            }
                        }
                    }
                }
            }

            return flag;
        }

        /// <summary>
        /// 判断是否是孤立线
        /// </summary>
        /// <param name="line">要判断的线</param>
        /// <param name="verticalLines">垂直线集合</param>
        /// <param name="minLength">最小长度</param>
        /// <returns>是否是孤立线</returns>
        private static bool isIsolateLine(Line line, List<Line> verticalLines, int minLength)
        {
            if (line.BMax - line.BMin > minLength)
            {
                return false;
            }

            var verticalL = verticalLines.FindAll(l => l.A > line.BMin - 50 && l.A < line.BMax + 50);
            if (verticalL.Count > 0)
            {
                foreach (var l in verticalL)
                {
                    //其它任何直线的点不落在基线的50像素范围内
                    if ((l.BMin < line.A - 50 && l.BMax >= line.A - 50) || (l.BMin <= line.A + 50 && l.BMax > line.A + 50))
                    {
                        return false;
                    }
                }
            }
            else
            {
                return false;
            }

            return true;
        }

        /// <summary>
        /// 得到两条直线中较短的直线
        /// </summary>
        /// <param name="line1">第一条直线</param>
        /// <param name="line2">第二条直线</param>
        /// <returns>较短的直线</returns>
        private static Line getSortLine(Line line1, Line line2)
        {
            var l1 = line1.BMax - line1.BMin;
            var l2 = line2.BMax - line2.BMin;

            return l1 > l2 ? line2 : line1;
        }

        /// <summary>
        /// 去边界噪音线
        /// </summary>
        /// <param name="hLines">所有横线</param>
        /// <param name="vLines">所有竖线</param>
        /// <param name="w">图像宽度</param>
        /// <param name="h">图像高度</param>
        public static void DeleteEdgeLines(ref List<Line> hLines, ref List<Line> vLines, int w, int h)
        {
            int edgeAreaWl;
            int edgeAreaWr;
            int edgeAreaHt;
            int edgeAreaHb;

            if (w > h)
            {
                edgeAreaWl = w / 26;
                edgeAreaWr = w / 26 * 25;
                edgeAreaHt = h / 21;
                edgeAreaHb = h / 21 * 20;
            }
            else
            {
                edgeAreaWl = w / 21;
                edgeAreaWr = w / 21 * 20;
                edgeAreaHt = h / 26;
                edgeAreaHb = h / 26 * 25;
            }

            for (int i = 0; i < hLines.Count; i++)
            {
                var hLine = hLines[i];
                if (hLine.A < edgeAreaHt || hLine.A > edgeAreaHb)
                {
                    if (hLine.A < edgeAreaHt)
                    {
                        var crossVLines =
                        vLines.FindAll(
                            l =>
                            (l.A < ((hLine.BMax > edgeAreaWr) ? edgeAreaWr : hLine.BMax)) &&
                            (l.A > ((hLine.BMin < edgeAreaWl) ? edgeAreaWl : hLine.BMin)) && (l.BMin <= hLine.A));

                        if (crossVLines.Count > 0)
                        {
                            continue;
                        }
                        hLines.Remove(hLine);
                        i--;
                    }
                    else if (hLine.A > edgeAreaHb)
                    {
                        var crossVLines =
                        vLines.FindAll(
                            l =>
                            (l.A < ((hLine.BMax > edgeAreaWr) ? edgeAreaWr : hLine.BMax)) &&
                            (l.A > ((hLine.BMin < edgeAreaWl) ? edgeAreaWl : hLine.BMin)) && (l.BMax >= hLine.A));

                        if (crossVLines.Count > 0)
                        {
                            continue;
                        }
                        hLines.Remove(hLine);
                        i--;
                    }
                }
            }

            for (int i = 0; i < vLines.Count; i++)
            {
                var vLine = vLines[i];
                if (vLine.A < edgeAreaWl || vLine.A > edgeAreaWr)
                {
                    if (vLine.A < edgeAreaWl)
                    {
                        var crossHLines =
                        hLines.FindAll(
                            l =>
                            (l.A < ((vLine.BMax > edgeAreaHb) ? edgeAreaHb : vLine.BMax)) &&
                            (l.A > ((vLine.BMin < edgeAreaHt) ? edgeAreaHt : vLine.BMin)) && (l.BMin <= vLine.A));

                        if (crossHLines.Count > 0)
                        {
                            continue;
                        }
                        vLines.Remove(vLine);
                        i--;
                    }
                    else if (vLine.A > edgeAreaWr)
                    {
                        var crossHLines =
                        hLines.FindAll(
                            l =>
                            (l.A < ((vLine.BMax > edgeAreaHb) ? edgeAreaHb : vLine.BMax)) &&
                            (l.A > ((vLine.BMin < edgeAreaHt) ? edgeAreaHt : vLine.BMin)) && (l.BMax >= vLine.A));

                        if (crossHLines.Count > 0)
                        {
                            continue;
                        }
                        vLines.Remove(vLine);
                        i--;
                    }
                }
            }
        }

        #endregion

        #region 找线的交点

        /// <summary>
        /// 找线的所有交点并返回坐标
        /// </summary>
        /// <param name="hLines">所有横线</param>
        /// <param name="vLines">所有竖线</param>
        /// <returns>所有交点</returns>
        public static List<int[]> GetLinesNodes(List<Line> hLines, List<Line> vLines)
        {
            var nodeList = new List<int[]>();
            foreach (var hLine in hLines)
            {
                foreach (var vLine in vLines)
                {
                    var node = new int[2];
                    if (getNodeAtTwoLines(hLine, vLine, ref node))
                    {
                        nodeList.Add(node);
                    }
                }
            }
            return nodeList;
        }

        /// <summary>
        /// 判断两直线是否相交，并获取两直线的交点
        /// </summary>
        /// <param name="hLine">横线</param>
        /// <param name="vLine">竖线</param>
        /// <param name="node">两直线的交点坐标 </param>
        /// <returns>是否相交</returns>
        private static bool getNodeAtTwoLines(Line hLine, Line vLine, ref int[] node)
        {
            var flag = false;
            //注意临界值处理
            if ((hLine.BMin <= vLine.A + 1 && vLine.A - 1 <= hLine.BMax) && (vLine.BMin <= hLine.A + 1 && hLine.A - 1 <= vLine.BMax))
            {
                flag = true;
                node[0] = vLine.A;
                node[1] = hLine.A;
            }
            return flag;
        }

        #endregion

        #region 直线修正

        /// <summary>
        /// 直线修正补全方法-根据阈值探测相交线
        /// </summary>
        /// <param name="hLines">所有横线集合</param>
        /// <param name="vLines">所有竖线集合</param>
        /// <param name="x">阈值 允许两直线错开的距离</param>
        /// <returns>直线补全后的直线集合</returns>
        public static void CorrectLines(ref List<Line> hLines, ref List<Line> vLines, int x)
        {
            //探测修正：两直线端点相互靠近，在阈值范围内相遇则说明两直线为一条直线，前提是在探测过程中不能与第三条线相交
            //然后按长的直线坐标进行修正

            //横线探测补全
            for (int i = 0; i < hLines.Count; i++)
            {
                //横线向左探测
                hLines[i].BMin = leftCompleteHLine(hLines[i], vLines, x);

                //横线向右探测
                hLines[i].BMax = rightCompleteHLine(hLines[i], vLines, x);
            }

            //竖线探测补全
            for (int i = 0; i < vLines.Count; i++)
            {
                //竖线向上探测
                vLines[i].BMin = upCompleteVLine(vLines[i], hLines, x);

                //竖线向下探测
                vLines[i].BMax = downCompleteVLine(vLines[i], hLines, x);
            }
        }

        /// <summary>
        /// 向左补全横线
        /// </summary>
        /// <param name="hLine">要补全的横线</param>
        /// <param name="vLines">所有竖线集合</param>
        /// <param name="x">阈值</param>
        /// <returns>补全后的横线B_Min值</returns>
        private static int leftCompleteHLine(Line hLine, List<Line> vLines, int x)
        {
            for (int i = 0; i <= x; i++)
            {
                foreach (var vLine in vLines)
                {
                    //向左延伸
                    if (vLine.A == hLine.BMin - i)
                    {
                        //两线相交的条件
                        if ((vLine.BMin < hLine.A && vLine.BMax >= hLine.A - x) || (vLine.BMin <= hLine.A + x && vLine.BMax > hLine.A))
                        {
                            return vLine.A;
                        }
                    }
                    //向右收缩
                    if (vLine.A == hLine.BMin + i)
                    {
                        //两线相交的条件
                        if ((vLine.BMin < hLine.A && vLine.BMax >= hLine.A - x) || (vLine.BMin <= hLine.A + x && vLine.BMax > hLine.A))
                        {
                            return vLine.A;
                        }
                    }
                }
            }

            return hLine.BMin;
        }

        /// <summary>
        /// 向右补全横线
        /// </summary>
        /// <param name="hLine">要补全的横线</param>
        /// <param name="vLines">所有竖线集合</param>
        /// <param name="x">阈值</param>
        /// <returns>补全后的横线B_Max值</returns>
        private static int rightCompleteHLine(Line hLine, List<Line> vLines, int x)
        {
            for (int i = 0; i <= x; i++)
            {
                foreach (var vLine in vLines)
                {
                    //向右延伸
                    if (vLine.A == hLine.BMax + i)
                    {
                        //两线相交的条件
                        if ((vLine.BMin < hLine.A && vLine.BMax >= hLine.A - x) || (vLine.BMin <= hLine.A + x && vLine.BMax > hLine.A))
                        {
                            return vLine.A;
                        }
                    }
                    //向左收缩
                    if (vLine.A == hLine.BMax - i)
                    {
                        //两线相交的条件
                        if ((vLine.BMin < hLine.A && vLine.BMax >= hLine.A - x) || (vLine.BMin <= hLine.A + x && vLine.BMax > hLine.A))
                        {
                            return vLine.A;
                        }
                    }
                }
            }

            return hLine.BMax;
        }

        /// <summary>
        /// 向上补全竖线
        /// </summary>
        /// <param name="vLine">要补全的竖线</param>
        /// <param name="hLines">所有横线集合</param>
        /// <param name="x">阈值</param>
        /// <returns>补全后的竖线B_Min值</returns>
        private static int upCompleteVLine(Line vLine, List<Line> hLines, int x)
        {
            for (int i = 0; i <= x; i++)
            {
                foreach (var hLine in hLines)
                {
                    //向上延伸
                    if (hLine.A == vLine.BMin - i)
                    {
                        //两线相交的条件
                        if ((hLine.BMin < vLine.A && hLine.BMax >= vLine.A - x) || (hLine.BMin <= vLine.A + x && hLine.BMax > vLine.A))
                        {
                            return hLine.A;
                        }
                    }
                    //向下收缩
                    if (hLine.A == vLine.BMin + i)
                    {
                        //两线相交的条件
                        if ((hLine.BMin < vLine.A && hLine.BMax >= vLine.A - x) || (hLine.BMin <= vLine.A + x && hLine.BMax > vLine.A))
                        {
                            return hLine.A;
                        }
                    }
                }
            }

            return vLine.BMin;
        }

        /// <summary>
        /// 向下补全竖线
        /// </summary>
        /// <param name="vLine">要补全的竖线</param>
        /// <param name="hLines">所有横线集合</param>
        /// <param name="x">阈值</param>
        /// <returns>补全后的竖线B_Max值</returns>
        private static int downCompleteVLine(Line vLine, List<Line> hLines, int x)
        {
            for (int i = 0; i <= x; i++)
            {
                foreach (var hLine in hLines)
                {
                    //向下延伸
                    if (hLine.A == vLine.BMax + i)
                    {
                        //两线相交的条件
                        if ((hLine.BMin < vLine.A && hLine.BMax >= vLine.A - x) || (hLine.BMin <= vLine.A + x && hLine.BMax > vLine.A))
                        {
                            return hLine.A;
                        }
                    }
                    //向上收缩
                    if (hLine.A == vLine.BMax - i)
                    {
                        //两线相交的条件
                        if ((hLine.BMin < vLine.A && hLine.BMax >= vLine.A - x) || (hLine.BMin <= vLine.A + x && hLine.BMax > vLine.A))
                        {
                            return hLine.A;
                        }
                    }
                }
            }

            return vLine.BMax;
        }

        #endregion

        /// <summary>
        /// 判断两直线是否属于同一簇
        /// </summary>
        /// <param name="line1">直线1</param>
        /// <param name="line2">直线2</param>
        /// <returns>是否同簇</returns>
        private static bool isOnSameCluster(Line line1, Line line2)
        {
            var flag = false;

            var lengthLine1 = line1.BMax - line1.BMin;
            var lengthLine2 = line2.BMax - line2.BMin;
            var rangeMax = line1.BMax > line2.BMax ? line1.BMax : line2.BMax;
            var rangeMin = line1.BMin < line2.BMin ? line1.BMin : line2.BMin;
            var rangeLength = rangeMax - rangeMin;//两条直线的范围长度

            //判断两直线是否同列条件：两直线的范围长度必小于两直线的总长度
            if (rangeLength < lengthLine1 + lengthLine2)
            {
                flag = true;
            }

            return flag;
        }

        #endregion

        #region 影像分析

        /// <summary>
        /// 根据线条分析影像类型
        /// </summary>
        /// <param name="hLines">横线集合</param>
        /// <param name="vLines">竖线集合</param>
        /// <param name="w">影像宽</param>
        /// <param name="h">影像高</param>
        /// /// <param name="rectangle">药品区域范围</param>
        /// <returns>影像类型</returns>
        public static ImageType AnalysicImageClass(List<Line> hLines, List<Line> vLines, int w, int h, ref Rectangle rectangle)
        {
            //*******************************************************
            //*目前所有影像定义为四类：
            //* 1、上海发票类影像--左右结构，左边有矩形框，右边有一条或两条横线
            //* 2、表格结构影像--明显的列表结构，所有字段在一个个表格格子内
            //* 3、带框结构影像--整个影像的重心是框架，内容在其中最大的矩形框内
            //* 4、普通结构影像--整个影像的行特征明显，有横线或没横线，线条之间没有什么规律
            //*******************************************************

            //影像类型
            var type = ImageType.Unknown;

            //分析是否是上海发票类
            var hLine = new Line();
            if (isShanghaiBill(hLines, vLines, ref hLine))
            {
                var vLine = leftDetectFindVLine(hLine, vLines);
                if (vLine.A != 0)
                {
                    type = ImageType.Bill;
                    rectangle.X = vLine.A + 10;
                    rectangle.Y = hLine.A + 10;
                    rectangle.Width = hLine.BMax - vLine.A - 10;
                    rectangle.Height = vLine.BMax - hLine.A - 20;
                }
            }
            else if (isTableDetailImage(hLines, vLines))
            {
                type = ImageType.TableDetail;
                var x = vLines[0].A;
                x = vLines.Select(vLine => vLine.A).Concat(new[] { x }).Min();
                var y = hLines[0].A;
                y = hLines.Select(line => line.A).Concat(new[] { y }).Min();
                var x1 = vLines[0].A;
                x1 = vLines.Select(vLine => vLine.A).Concat(new[] { x1 }).Max();
                var y1 = hLines[0].A;
                y1 = hLines.Select(line => line.A).Concat(new[] { y1 }).Max();
                rectangle = new Rectangle(x, y, x1 - x, y1 - y);
            }
            else if (isFrameDetailImage(hLines, vLines))
            {
                type = ImageType.FrameDetail;
                var nodes = GetLinesNodes(hLines, vLines);
                rectangle = findFrameImageContentArea(nodes);
                rectangle.X = rectangle.X + 5;
                rectangle.Y = rectangle.Y + 5;
                rectangle.Width = rectangle.Width - 5;
                rectangle.Height = rectangle.Height - 5;
            }
            else if (isNormalDetailImage(hLines, vLines))
            {
                type = ImageType.NormalDetail;
                rectangle = findNormalImageContentArea(hLines, vLines, w, h);
            }

            return type;
        }

        #region 上海影像判别方法

        /// <summary>
        /// 判断是否属于上海发票类
        /// </summary>
        /// <param name="hLines">横线集合</param>
        /// <param name="vLines">竖线集合</param>
        /// <param name="dLine">找到的横线基线</param>
        /// <returns>是否属于上海发票类</returns>
        private static bool isShanghaiBill(List<Line> hLines, List<Line> vLines, ref Line dLine)
        {
            //影像特征：首先找到一根横线，它的下面必有最少5根横线，它的右边（上下浮动不超10像素）必有一根横线，与它相交有两竖线
            if (hLines.Count < 7 || vLines.Count < 2 || hLines.Count > 10 || vLines.Count > 5)
            {
                return false;
            }

            foreach (var hLine in hLines)
            {
                //将基线长度向左向右各延伸60像素，可接受的误差
                var min = hLine.BMin - 60;
                var max = hLine.BMax + 60;

                var findLines = new List<Line>();
                foreach (var line in hLines)
                {
                    if (line.BMin >= min && line.BMax <= max)
                    {
                        findLines.Add(line);
                    }
                }

                if (findLines.Count >= 4 && findLines.Count <= 10)
                {
                    if (isFindHLine(hLines, hLine, ref dLine))
                    {
                        return true;
                    }
                }
            }

            return false;
        }

        /// <summary>
        /// 向右探测是否找到横线，探测距离是基线长度的一半，探测浮动是20像素
        /// </summary>
        /// <param name="hLines">横线集合</param>
        /// <param name="line">基线</param>
        /// <param name="dLine">找到的横线基线</param>
        /// <returns>是否找到</returns>
        private static bool isFindHLine(List<Line> hLines, Line line, ref Line dLine)
        {
            var x = (line.BMax - line.BMin) / 2;
            for (int i = 0; i < x; i++)
            {
                var newMax = line.BMax + i;
                foreach (var hLine in hLines)
                {
                    if (hLine.BMin == newMax)
                    {
                        if (hLine.A < line.A + 60 && hLine.A > line.A - 60)
                        {
                            dLine = hLine;
                            return true;
                        }
                    }
                }
            }

            return false;
        }

        /// <summary>
        /// 向左探测找竖线，探测距离是基线长度的一半
        /// </summary>
        /// <param name="line">基线</param>
        /// <param name="vLines">竖线集合</param>
        /// <returns>找到的竖线</returns>
        private static Line leftDetectFindVLine(Line line, List<Line> vLines)
        {
            var x = (line.BMax - line.BMin) / 2;//设置探测阈值
            var newLine = new Line();
            for (int i = 1; i <= x; i++)
            {
                var newMin = line.BMin - i;
                foreach (var vLine in vLines)
                {
                    if (vLine.A == newMin && (vLine.BMin >= line.A - 100 && vLine.BMin <= line.A + 100))//待测试
                    {
                        newLine = vLine;
                    }
                }
            }
            return newLine;
        }

        #endregion

        #region 表格结构影像判别方法

        private static bool isTableDetailImage(List<Line> hLines, List<Line> vLines)
        {
            if (hLines.Count < 10 || vLines.Count <= 2)
            {
                return false;
            }

            return true;
        }

        #endregion

        #region 带框影像判别方法

        private static bool isFrameDetailImage(List<Line> hLines, List<Line> vLines)
        {
            var nodes = GetLinesNodes(hLines, vLines);
            if (nodes.Count < hLines.Count * vLines.Count / 2)
            {
                return false;
            }
            if ((hLines.Count >= 4 && hLines.Count <= 7) && (vLines.Count >= 2 && vLines.Count <= 5))
            {
                return true;
            }

            return false;
        }

        /// <summary>
        /// 根据点分析带框影像药品区（所有点构成）
        /// </summary>
        /// <param name="nodes">直线相交节点</param>
        /// <returns>最大矩形框（即药品区域）</returns>
        private static Rectangle findFrameImageContentArea(List<int[]> nodes)
        {
            var rectangle = new Rectangle();

            foreach (var node in nodes)
            {
                var x = node[0];
                var y = node[1];

                var rightNodes = nodes.Where(n => n[1] == y && n[0] > x).OrderBy(d => d[0]);
                if (rightNodes.Any())
                {
                    foreach (var rightNode in rightNodes)
                    {
                        var lowerNodes = nodes.Where(n => n[1] > y && n[0] == x).OrderBy(d => d[1]);
                        if (lowerNodes.Any())
                        {
                            foreach (var lowerNode in lowerNodes)
                            {
                                if (nodes.Exists(n => n[0] == rightNode[0] && n[1] == lowerNode[1]))
                                {
                                    if ((rightNode[0] - x) * (lowerNode[1] - y) > rectangle.Width * rectangle.Height)
                                    {
                                        rectangle.X = x;
                                        rectangle.Y = y;
                                        rectangle.Width = rightNode[0] - x;
                                        rectangle.Height = lowerNode[1] - y;
                                    }
                                    goto Found;
                                }
                            }
                        }
                    }
                }
            Found:
                ;

            }

            return rectangle;
        }

        #endregion

        #region 普通结构影像判别方法

        private static bool isNormalDetailImage(List<Line> hLines, List<Line> vLines)
        {
            var flag = false;

            if (hLines.Count > 0 && vLines.Count <= 2)
            {
                flag = true;
            }
            else if (hLines.Count <= 2 && vLines.Count > 0)
            {
                flag = true;
            }

            return flag;
        }

        /// <summary>
        /// 根据点分析带框影像药品区（所有点构成）
        /// </summary>
        /// <returns>最大矩形框（即药品区域）</returns>
        private static Rectangle findNormalImageContentArea(List<Line> hLines, List<Line> vLines, int w, int h)
        {
            var rectangle = new Rectangle(0, 0, w, h);

            if (vLines.Count <= 1 && hLines.Count > 0)
            {
                var max = hLines[0].BMax - hLines[0].BMin;
                var maxHline = hLines[0];
                foreach (var hLine in hLines)
                {
                    if (max < (hLine.BMax - hLine.BMin))
                    {
                        max = hLine.BMax - hLine.BMin;
                        maxHline = hLine;
                    }
                }

                //找第一根横线
                var topLine = hLines[0];
                foreach (var hLine in hLines)
                {
                    if (topLine.A > hLine.A)
                    {
                        topLine = hLine;
                    }
                }

                if (max > w / 3 * 2)
                {
                    rectangle = new Rectangle(maxHline.BMin, topLine.A, max, h - topLine.A);
                }
            }
            else if (hLines.Count <= 1 && vLines.Count > 0)
            {
                var max = vLines[0].BMax - vLines[0].BMin;
                var maxHline = vLines[0];
                foreach (var vLine in vLines)
                {
                    if (max < (vLine.BMax - vLine.BMin))
                    {
                        max = vLine.BMax - vLine.BMin;
                        maxHline = vLine;
                    }
                }

                if (max > w / 3 * 2)
                {
                    rectangle = new Rectangle(0, maxHline.BMin, w, max);
                }
            }

            return rectangle;
        }

        #endregion

        #region 药品区投影分析找行列

        /// <summary>
        /// 分析投影，找到行区域
        /// </summary>
        /// <param name="gs">投影二值矩阵</param>
        /// <param name="w">投影宽</param>
        /// <param name="h">投影高</param>
        /// <param name="x">阈值 允许两行间相邻最大像素</param>
        /// <param name="hArea">阈值 超过此值即认为是一行</param>
        /// <returns>意义行矩形</returns>
        public static List<Rectangle> GetRowArea(byte[,] gs, int w, int h, int x, int hArea)
        {
            var rs = new List<Rectangle>();
            var n = 0;
            var b = 0;
            for (int j = 0; j < h; j++)
            {
                if (gs[x, j] == 1)
                {
                    n++;
                    b = 0;
                }
                else
                {
                    if (n >= hArea)//超过hArea像素则认为是一行
                    {
                        var y1 = j - n - 10 < 0 ? 0 : j - n - 10;//边界补充修正，避免字体刚好压线
                        var y2 = j + 10 > h - 1 ? h - 1 : j + 10;//边界补充修正，避免字体刚好压线
                        var r = new Rectangle(0, y1, w, y2 - y1);
                        rs.Add(r);
                    }

                    n = 0;

                    b++;

                    //去除药品内容区底部杂乱信息干扰，超出50像素空白则认为后面信息为干扰信息
                    if (b > 50 && rs.Count > 1)
                    {
                        break;
                    }
                }
            }

            return rs;
        }

        /// <summary>
        /// 分析列，将每行进行投影分成列-分段法
        /// </summary>
        /// <param name="im">影像二值矩阵</param>
        /// <param name="rectangles">行矩形</param>
        /// <returns>分成列的行矩形列表</returns>
        public static List<List<Rectangle>> AnalysisColumn2(byte[,] im, List<Rectangle> rectangles)
        {
            var segmentList = new List<List<int[]>>();//分段列表
            for (int i = 0; i < rectangles.Count; i++)
            {
                var gs = rectangleProjection(im, rectangles[i]);
                var intobjs = rectangleProjectionAnalysis2(gs);
                if (intobjs.Count <= 6)
                {
                    segmentList.Add(intobjs);
                }
                else
                {
                    rectangles.Remove(rectangles[i]);
                    i--;
                }
            }

            //根据断点将行矩形分列
            var listRectangles = new List<List<Rectangle>>();
            var listRectangle = new List<Rectangle>();
            for (int i = 0; i < rectangles.Count; i++)
            {
                foreach (var segment in segmentList[i])
                {
                    var x = segment[0] - 15 < 0 ? 0 : segment[0] - 15;
                    var y = rectangles[i].Y;
                    var w = segment[1] + 30 + x > rectangles[i].Width - 1 ? rectangles[i].Width - x - 1 : segment[1] + 30;
                    var h = rectangles[i].Height;

                    var r = new Rectangle(x, y, w, h);
                    listRectangle.Add(r);
                }
                listRectangles.Add(listRectangle);
                listRectangle = new List<Rectangle>();
            }

            return listRectangles;
        }

        /// <summary>
        /// 分析列，将每行进行投影分成列-分段法，只保留第一列
        /// </summary>
        /// <param name="im">影像二值矩阵</param>
        /// <param name="rectangles">行矩形</param>
        /// <returns>分成列的行矩形列表</returns>
        public static List<List<Rectangle>> AnalysisColumn3(byte[,] im, List<Rectangle> rectangles)
        {
            var segmentList = new List<List<int[]>>();//分段列表
            for (int i = 0; i < rectangles.Count; i++)
            {
                var gs = rectangleProjection(im, rectangles[i]);
                var intobjs = rectangleProjectionAnalysis3(gs);
                if (intobjs.Count <= 6)
                {
                    segmentList.Add(intobjs);
                }
                else
                {
                    rectangles.Remove(rectangles[i]);
                    i--;
                }
            }

            //多行处理
            moreLinesProcessing(ref segmentList, ref rectangles);

            //只留药品名称区
            for (int i = 0; i < segmentList.Count; i++)
            {
                var ints = segmentList[i][0];
                segmentList[i].RemoveAll(l => l != null);
                segmentList[i].Add(ints);
            }

            //根据断点将行矩形分列
            var listRectangles = new List<List<Rectangle>>();
            var listRectangle = new List<Rectangle>();
            for (int i = 0; i < rectangles.Count; i++)
            {
                foreach (var segment in segmentList[i])
                {
                    var x = segment[0] - 8 < 0 ? 0 : segment[0] - 8;
                    var y = rectangles[i].Y;
                    var w = segment[1] + 16 + x > rectangles[i].Width - 1 ? rectangles[i].Width - x - 1 : segment[1] + 16;
                    var h = rectangles[i].Height;

                    var r = new Rectangle(x, y, w, h);
                    listRectangle.Add(r);
                }
                listRectangles.Add(listRectangle);
                listRectangle = new List<Rectangle>();
            }

            return listRectangles;
        }

        /// <summary>
        /// 对行矩形进行横向投影
        /// </summary>
        /// <param name="im">影像二值矩阵</param>
        /// <param name="rectangle">行矩形</param>
        /// <returns>投影后的二值矩阵</returns>
        private static byte[,] rectangleProjection(byte[,] im, Rectangle rectangle)
        {
            var rIm = new byte[rectangle.Width, rectangle.Height];

            for (int j = rectangle.Y; j < rectangle.Y + rectangle.Height; j++)
            {
                for (int i = 0; i < rectangle.Width; i++)
                {
                    rIm[i, j - rectangle.Y] = im[i, j];
                }
            }

            return VerticalProjection(rIm);
        }

        /// <summary>
        /// 矩形分列投影分析--分段法
        /// </summary>
        /// <param name="gs">矩阵块投影二值矩阵</param>
        /// <returns>分段数组列表[起始点，长度]</returns>
        private static List<int[]> rectangleProjectionAnalysis2(byte[,] gs)
        {
            var w = gs.GetLength(0);
            var h = gs.GetLength(1);

            var ws = new List<int[]>();
            var n = 0;
            var b = 0;
            for (int i = 0; i < w; i++)
            {
                if (gs[i, h - 1] == 1)
                {
                    n++;
                    if (b > 0 && b <= 27)
                    {
                        n = n + b;
                        b = 0;
                    }
                }
                else
                {
                    if (n > 0)
                    {
                        b++;
                    }
                }

                if (b > 27)//空白区域长度超过27像素，则认为是另起一列，并记录当前断点
                {
                    if (n > 16)
                    {
                        var intobj = new int[2];
                        intobj[0] = i - b - n;
                        intobj[1] = n;
                        ws.Add(intobj);
                    }

                    n = 0;
                    b = 0;
                }
                else if (i == w - 1 && n > 64)//边界处理
                {
                    var intobj = new int[2];
                    intobj[0] = i - n;
                    intobj[1] = n;
                    ws.Add(intobj);
                }
            }

            //所有内容在一个矩形内的情况
            if (ws.Count == 0)
            {
                var intobj = new int[2];
                intobj[0] = 0;
                intobj[1] = w;
                ws.Add(intobj);
            }

            return ws;
        }

        /// <summary>
        /// 矩形分列投影分析--逐行分析法
        /// </summary>
        /// <param name="gs">矩阵块投影二值矩阵</param>
        /// <returns>分段数组列表[起始点，长度]</returns>
        private static List<int[]> rectangleProjectionAnalysis3(byte[,] gs)
        {
            var w = gs.GetLength(0);
            var h = gs.GetLength(1);

            var ws = new List<int[]>();
            var n = 0;
            var b = 0;
            for (int i = 0; i < w; i++)
            {
                if (gs[i, h - 1] == 1)
                {
                    n++;
                    if (b > 0 && b <= 27)
                    {
                        n = n + b;
                        b = 0;
                    }
                }
                else
                {
                    if (n > 0)
                    {
                        b++;
                    }
                }

                if (b > 27)//空白区域长度超过27像素，则认为是另起一列，并记录当前断点
                {
                    if (n > 16)
                    {
                        //分段数组[起始点，长度]
                        var intobj = new int[2];
                        intobj[0] = i - b - n;
                        intobj[1] = n;
                        ws.Add(intobj);
                    }

                    n = 0;
                    b = 0;
                }
                else if (i == w - 1 && n > 64)//边界处理
                {
                    var intobj = new int[2];
                    intobj[0] = i - n;
                    intobj[1] = n;
                    ws.Add(intobj);
                }
            }

            //所有内容在一个矩形内的情况
            if (ws.Count == 0)
            {
                var intobj = new int[2];
                intobj[0] = 0;
                intobj[1] = w;
                ws.Add(intobj);
            }

            return ws;
        }

        /// <summary>
        /// 药品名占多行处理方法
        /// </summary>
        /// <param name="listIntses">分段数组列表</param>
        /// <param name="rectangles">矩形行列表</param>
        private static void moreLinesProcessing(ref List<List<int[]>> listIntses, ref List<Rectangle> rectangles)
        {
            for (int i = 1; i < listIntses.Count; i++)
            {
                var count1 = listIntses[i - 1].Count();
                var count2 = listIntses[i].Count();

                if (count1 >= 3 && count2 <= 2)
                {
                    if (count2 == 1 && listIntses[i][0][1] > rectangles[i].Width / 2)
                    {
                        continue;
                    }
                    var maxLength = listIntses[i - 1][0][1] > listIntses[i][0][1] ? listIntses[i - 1][0][1] : listIntses[i][0][1];
                    listIntses[i - 1][0][1] = maxLength;
                    listIntses.Remove(listIntses[i]);

                    //同步处理矩形，合并行矩形，重置高度
                    var newHeight = rectangles[i - 1].Height + rectangles[i].Height;
                    rectangles[i - 1] = new Rectangle(rectangles[i - 1].X, rectangles[i - 1].Y, rectangles[i - 1].Width, newHeight);
                    rectangles.Remove(rectangles[i]);
                }
            }
        }

        #endregion

        #endregion
    }
}
